﻿using Nemerle.Text;
using Nemerle.Utility;
using Nemerle.Data;

using System;
using System.Collections.Generic;
using System.Console;
using System.Linq;

using FsCheck.Fluent;

namespace QCTests
{
  public module TestSeq
  {
    public Run() : void
    {
      Spec.ForAny(xs : array[bool] => xs.AndSeq() == xs.All(x => x))
        .QuickCheck("AndSeq");
        
      Spec.ForAny(xs : array[int] => xs.AsSeq().SequenceEqual(xs) && xs.AsSeq() is array[int])
        .QuickCheck("AsSeq");
        
      Spec.ForAny(xss : array[array[int]] => xss.ConcatenateSeq().SequenceEqual(xss.SelectMany(xs => xs)))
        .QuickCheck("ConcatenateSeq");
        
      Spec.ForAny(xs : array[array[int]] => xs.ConcatMapSeq(x => array[x, x]).SequenceEqual(xs.SelectMany(x => array[x, x])))
        .QuickCheck("ConcatMapSeq");
        
      Spec.ForAny((xs : array[int], x : int) => xs.ContainsElemSeq(x) == xs.Any(_ == x))
        .QuickCheck("ContainsElemSeq");
        
      //Spec.ForAny(xs : array[int] => xs.Copy().SequenceEqual(xs))
      //  .QuickCheck("Copy");
        
      //Spec.ForAny((xs : array[int], n : int) => xs.DropSeq(n).SequenceEqual(xs.Skip(n)))
      //  .QuickCheck("Drop");
      
      //Spec.ForAny((xs : array[int], n : int) => xs.DropAtMostSeq(n).SequenceEqual(xs.Skip(n)))
      //  .QuickCheck("DropAtMost");  
      
      Spec.ForAny((xs : array[int], n : int) => xs.DropExactlySeq(n).SequenceEqual(xs.Skip(n)))
        .When((xs,n) => n <= xs.Length && n >= 0)
        .QuickCheck("DropExactlySeq");
        
      //Spec.ForAny((xs : array[int], n : int) => xs.DropLastsSeq(n).SequenceEqual(xs.Reverse().Skip(n).Reverse()))
      //  .QuickCheck("DropLasts");
        
      //Spec.ForAny((xs : array[int], x : int) => x.ElementOfSeq(xs) == xs.Any(_ == x))
      //  .QuickCheck("ElementOf");
        
      WriteLine(if (Seq.Empty.[int]().SequenceEqual(array(0) : array[int])) "Seq.Empty-Ok, passed 1 test." else "Empty-Falsifiable, after 1 test.");
      
      Spec.ForAny((xs : array[int], x : int) => xs.ExistSeq(_ > x) == xs.Any(_ > x))
        .QuickCheck("ExistSeq");
        
      Spec.ForAny((xs : array[int], x : int) => xs.FilterSeq(_ > x).SequenceEqual(xs.Where(_ > x)))
        .QuickCheck("FilterSeq");
        
      Spec.ForAny(xs : array[string] => xs.FilterISeq((i, x) => x.Length < i).SequenceEqual(xs.Where((x, i) => x.Length < i)))
        .QuickCheck("FilterISeq");
        
      Spec.ForAny((xs : array[int], x : int) => xs.FindSeq(_ > x).IsSome == xs.Any(_ > x))
        .And((xs, x) => if (xs.Find(_ > x).IsSome) xs.FindSeq(_ > x).Value > x else true )
        .QuickCheck("FindSeq");
        
      Spec.ForAny((xs : array[char], x : string) => xs.FoldSeq(x, (s, c) => s + c.ToString()) == xs.Aggregate(x, (s, c) => s + c.ToString()))
        .QuickCheck("FoldSeq");
        
      Spec.ForAny((xs : array[char], x : string) => xs.FoldISeq(x, (i, s, c) => s + c.ToString() + i.ToString()) == 
        xs.Select((x, i) => (x, i)).Aggregate(x, (s, (c, i)) => s + c.ToString() + i.ToString()))
        .QuickCheck("FoldISeq");
        
      //Spec.ForAny((xs : array[char], x : string) => xs.FoldRightSeq(x, (c, s) => s + c.ToString()) == xs.Reverse().Aggregate(x, (s, c) => s + c.ToString()))
      //  .QuickCheck("FoldRightSeq");
        
      //Spec.ForAny(xs : array[string] => xs.FoldRight(_ + _) == xs.Reverse().Aggregate(_ + _))
      //  .When(xs => xs.Length > 0)
      //  .QuickCheck("FoldRight1");
        
      //Spec.ForAny((xs : array[char], x : string) => xs.FoldRightI(x, (i, c, s) => s + c.ToString() + i.ToString()) == 
      //  xs.Reverse().Select((x, i) => (x, i)).Aggregate(x, (s, (c, i)) => s + c.ToString() + i.ToString()))
      //  .QuickCheck("FoldRightI");
        
      Spec.ForAny((xs : array[int], x : int) => xs.ForAllSeq(_ > x) == xs.All(_ > x))
        .QuickCheck("ForAllSeq");
        
      //Spec.ForAny(xs : array[int] => xs.HeadSeq() == xs[0])
      //  .When(xs => xs.Length > 0)
      //  .QuickCheck("Head");
        
      //Spec.ForAny(xs : array[int] => xs.InitSeq().SequenceEqual(xs.Reverse().Skip(1).Reverse()))
      //  .QuickCheck("Init");
      
      Spec.ForAny(xs : array[int] => { mutable summ = 0; xs.AsSeq().Iter(x => summ += x); summ == xs.Sum() })
        .QuickCheck("Iter");
        
      Spec.ForAny(xs : array[int] => { mutable summ = 0; xs.ToSeq().Iter(x => summ += x); summ == xs.Sum() })
        .QuickCheck("Iter2");
        
      Spec.ForAny(xs : array[int] => { mutable summ = 0; xs.IterSeq(x => summ += x); summ == xs.Sum() })
        .QuickCheck("IterSeq");
        
      Spec.ForAny(xs : array[string] => { def lst = List(); xs.AsSeq().IterI((i, x) => lst.Add(x + i.ToString())); lst.SequenceEqual(xs.Select((x, i) => x + i.ToString()))})
        .QuickCheck("IterI");
        
      Spec.ForAny(xs : array[string] => { def lst = List(); xs.ToSeq().IterI((i, x) => lst.Add(x + i.ToString())); lst.SequenceEqual(xs.Select((x, i) => x + i.ToString()))})
        .QuickCheck("IterI2");
      
      Spec.ForAny(xs : array[string] => { def lst = List(); xs.IterISeq((i, x) => lst.Add(x + i.ToString())); lst.SequenceEqual(xs.Select((x, i) => x + i.ToString()))})
        .QuickCheck("IterISeq");
        
      //Spec.ForAny(xs : array[int] => xs.IsEmptySeq() == !xs.Any())
      //  .QuickCheck("IsEmpty");
      
      //Spec.ForAny(xs : array[int] => xs.LastElemSeq() == xs.Last())
      //  .QuickCheck("LastElem");
        
      Spec.ForAny(xs : array[int] => xs.MapSeq(x => -x).SequenceEqual(xs.Select(x => -x)))
        .QuickCheck("MapSeq");
        
      def zipf(x, y) { x.ToString() + y };   
      
      Spec.ForAny((xs : array[int], ys : array[string]) => {
        def l = Math.Min(xs.Length, ys.Length);
        def xs' = xs.Take(l).ToArray();
        def ys' = ys.Take(l).ToArray();
        xs'.Map2Seq(ys', zipf).SequenceEqual(xs'.Select((x, i) => zipf(x, ys'[i])))
      })
        .QuickCheck("Map2Seq");
              
      Spec.ForAny(xs : array[string] => xs.MapISeq((i, x) => i.ToString() + x).SequenceEqual(xs.Select((x, i) => i.ToString() + x)))
        .QuickCheck("MapISeq");
        
      Spec.ForAny(xs : array[bool] => xs.OrSeq() == xs.Any(x => x))
        .QuickCheck("OrSeq");
        
      Spec.ForAny(xs : array[string] => xs.ReduceSeq(_ + _) == xs.Aggregate(_ + _))
        .When(xs => xs.Length > 0)
        .QuickCheck("ReduceSeq");
        
      Spec.ForAny((x : string, n : int) => Seq.Replicate(x, n).SequenceEqual(Enumerable.Repeat(x, n)))
        .When((_, n) => n > 0)
        .QuickCheck("Replicate_1");
        
      Spec.ForAny((x : string, n : int) => Seq.Replicate(x, n).SequenceEqual(Seq.Empty()))
        .When((_, n) => n <= 0)
        .QuickCheck("Replicate_2");
        
      //Spec.ForAny(xs : array[int] => xs.Rev().Rev().SequenceEqual(xs))
      //  .QuickCheck("Rev");
      
      Spec.ForAny((xs : array[int], i : int) => xs.ScanSeq("", (s, x) => s + x.ToString()).ElementAt(i) == 
        xs.Take(i).Aggregate("", (s, x) => s + x.ToString()))
        .When((xs, i) => i < xs.Length - 1 && i >= 0)
        .QuickCheck("ScanSeq");
        
      //Spec.ForAny((xs : array[int], i : int) => xs.ScanRightSeq("", (x, s) => s + x.ToString()).ElementAt(i) == 
      //  xs.Reverse().Take(i).Aggregate("", (s, x) => s + x.ToString()))
      //  .When((xs, i) => i < xs.Length - 1 && i >= 0)
      //  .QuickCheck("ScanRight");
        
      Spec.ForAny(x : string => Seq.Single(x).SequenceEqual(array[x]))
        .QuickCheck("Seq.Single");
        
      Spec.ForAny(xs : array[int] => xs.TailSeq().SequenceEqual(xs.Skip(1)))
        .When(xs => xs.Length > 0)
        .QuickCheck("TailSeq");
        
      //Spec.ForAny((xs : array[int], n : int) => xs.TakeAtMostSeq(n).SequenceEqual(xs.Take(n)))
      //  .QuickCheck("TakeAtMost");
        
      Spec.ForAny((xs : array[int], n : int) => xs.TakeExactlySeq(n).SequenceEqual(xs.Take(n)))
        .When((_, n) => n >= 0)
        .When((xs, n) => xs.Length >= n)
        .QuickCheck("TakeExactlySeq");
        
      Spec.ForAny(xs : array[int] => (xs : IEnumerable[int]).ToNList().SequenceEqual(xs))
        .QuickCheck("ToNList");
        
      Spec.ForAny(xs : array[int] => xs.AsSeq().ToSeq().SequenceEqual(xs) && !(xs.AsSeq().ToSeq() is array[int]))
        .QuickCheck("ToSeq");
        
      Spec.ForAny(xs : array[int] => xs.AsSeq().GetEnumerator().ToSeq().SequenceEqual(xs))
        .QuickCheck("IEnumerator.ToSeq");
        
      Spec.ForAny(xs : array[int] => Seq.Unfold(xs.ToSeq().GetEnumerator(), s => if (s.MoveNext()) Some((s, s.Current)) else None())
        .SequenceEqual(xs))
        .QuickCheck("Seq.Unfold_1");
        
      Spec.ForAny(n : int => Seq.Unfold(0, s => if (s <= n) Some((s + 1, s)) else None()).SequenceEqual($[0 .. n]))
        .QuickCheck("Seq.Unfold_2");
        
      Spec.ForAny((xs : array[int], ys : array[string]) => xs.ZipSeq(ys).SequenceEqual(xs.Select((x, i) => (x, ys[i]))))
        .When((xs, ys) => xs.Length <= ys.Length)
        .QuickCheck("ZipSeq_1");
        
      Spec.ForAny((xs : array[int], ys : array[string]) => xs.ZipSeq(ys).SequenceEqual(ys.Select((y, i) => (xs[i], y))))
        .When((xs, ys) => xs.Length >= ys.Length)
        .QuickCheck("ZipSeq_2");
        
      Spec.ForAny((xs : array[int], ys : array[string]) => xs.ZipWithSeq(ys, zipf).SequenceEqual(xs.Select((x, i) => zipf(x, ys[i]))))
        .When((xs, ys) => xs.Length <= ys.Length)
        .QuickCheck("ZipMapSeq_1");
        
      Spec.ForAny((xs : array[int], ys : array[string]) => xs.ZipWithSeq(ys, zipf).SequenceEqual(ys.Select((y, i) => zipf(xs[i], y))))
        .When((xs, ys) => xs.Length >= ys.Length)
        .QuickCheck("ZipMapSeq_2");
    }
  }
}
